program  

1.1 log @Initial revision  text @% This is TIE.WEB %————————————————————

% Version 0.1 ported from PASCAL (NR) 24 August 87

% Here is TeX material that gets inserted after \input webmac @i tie.preamble

@* Introduction.

\noindent Whenever a programmer wants to change a given \.{WEB program because of system dependencies, he will create a new change file. In addition there may be a second change file to modify system independent modules of the program. But the \.{WEB file cannot be tangled and weaved with more than one change file simultaneously. Therefore, we introduce the present program to merge a \.{WEB file and several change files producing a new \.{WEB file. Since the input files are tied together, the program is called \.{TIE. Furthermore, the program can be used to merge several change files giving a new single change file. This method seems to be more important because it doesn’t modify the original source file. The use of \.{TIE can be expanded to other programming languages since this processor only knows about the structure of change files and does not interpret the master file at all.

The program \.{TIE has to read lines from several input files to bring them in some special ordering. For this purpose an algorithm is used which looks a little bit complicated. But the method used only needs one buffer line for each input file. Thus the storage requirement of \.{TIE does not depend on the input data.

The program uses only few features of the local \PASCAL\ compiler that may need to be changed in other installations. The changes needed may be similar to those used to change the \.{WEB processors \.{TANGLE and \.{WEAVE. All places where changes may be needed are listed in the index under the entry “system dependencies”. @!@^system dependencies@>

This version of \.{TIE has been ported from a \PASCAL version {$\copyright$1983, 1984, 1986 by Technische Hochschule Darmstadt, Fachbereich Informatik, Institut f\"ur Theoretische Informatik. No attempt has been made to take advantage of the many nice features of C (for example to remove the restriction on the number of change files). There is no warranty, express or implied.

The “banner line” defined here should be changed whenever \.{TIE is modified. This program is put into the public domain. Nevertheless the copyright notice must not be replaced or modified.

@d banner = "This is TIE, C Version 0.1 (ported to C)"

@ Here is where \.{TIE starts, and where it ends. If a command line interface is present, changes to initialize the access might be needed here. @^system dependencies@> @^command line processing@>

@c #include <stdio.h> @<Input and output types@>@; @<Types in the outer block@>@; @<Globals in the outer block@>@; @<Error handling procedures@>@; @# main (argc, argv) int argc; char **argv; { int i,j;

@<Set initial values@>@; print_ln(banner); /* print a “banner line” */ @<Read the arguments, setting the master file, the change files, and any options@>; @<Initialize the input system@>; @<Process the input@>; @<Check that all changes have been read@>; /* here files should be closed if the operating system requires it */ @<Print the job |history|@>; jump_out();

@ The guts of the program are simple: we drag each line through the mud: @<Process the input@>= input_has_ended=false; while (!input_has_ended || (actual_input!=0)) process_line(); if (out_mode == post) /* last line has been changed */ write_string(out_file,"@@z\n"); @<Write carriage return for end of counts@>;

@ At the end of the program, we will tell the user if a change file had a line that didn’t match any relevant line in the master file.

@<Check that all changes have been read@>= for (i=1; i<next_available_file; i++) /* all change files */ if (input_organization[i].mode != ignore) err_print("! Change file entry did not match",i); @.Change file entry ...@>

@ Here are some macros for common programming idioms.

@d loop = while (true) @d do_nothing = /* empty statement */ @f loop while

@ The following parameters should be sufficient for most applications of \.{TIE. Note that |max_file_index| should not be enlarged without changing the processing of change file names. @^system dependencies@>

@d buf_size = 100 /* maximum length of one input line*/ @d max_file_index = 9/* we dont think that anyone needs more than 9 change files*/ @d max_name_length = 54 /* adapt this to your local name space*/

@* Handling multiple change files. We read the master and change files from the argument vector. The master file is first, followed by (eventually any number) up to 9 change files. The only options are |"-m"| to produce a master file, and |"-c"| to produce a change file. The last one wins.

@<Read the arguments...@>= while (–argc>0) { if (**++argv==’-’) switch (*++*argv) { case ’m’: prod_chf=false; break; case ’c’: prod_chf=true; break; default: fprintf(stderr, "Bad option -%c ignored\n", **argv); break; else { if (next_available_file > max_file_index) fatal_error("too many files"); input_organization[next_available_file++].name_of_file=*argv;

@ |next_available_file| tells us what file numebr is available as a change file. @<Globals...@>= int next_available_file=0; @i datadecl.web

@*Routines that test input/output conditions.

\noindent Here’s a simple function that checks if two lines are different.

@c boolean lines_dont_match(i,j) int i,j; { buffer_index k, lmt; if (input_organization[i].limit != input_organization[j].limit) return true; lmt=input_organization[i].limit; for (k=0; k<lmt; k++) if (input_organization[i].buffer[k] != input_organization[j].buffer[k]) return true; return false; @ @<Globals...@>=boolean lines_dont_match(); @ The function |e_of_ch_module| returns true if the input line from file |i| is equal to \.{@@z.

@c boolean e_of_ch_module(i) int i; { return ((input_organization[i].limit>=2) && (input_organization[i].buffer[0]==’@@’) && ((input_organization[i].buffer[1]==’Z’) || (input_organization[i].buffer[1]==’z’)));

@ @<Globals...@>=boolean e_of_ch_module();

@ The function |e_of_ch_preamble| returns true if the input line from file |i| is equal to \.{@@y.

@c boolean e_of_ch_preamble(i) int i; { return ((input_organization[i].limit>=2) && (input_organization[i].buffer[0]==’@@’) && ((input_organization[i].buffer[1]==’Y’) || (input_organization[i].buffer[1]==’y’)));

@ @<Globals...@>=boolean e_of_ch_module();

@*Routines that manipulate input and output.

@ We continue with a function to open a text file. Success is indicated by a boolean result. We assume that empty files can be handled like non existent ones. @^system dependencies@>

@c boolean file_open(f,name) text_file *f; name_type name; { return ((*f=fopen(name,"r"))!=NULL);

@ @<Globals...@>=boolean file_open();

@ This procedure opens all the input files.

@c @<Procedures for |open_input|@>@;@% open_input() /* prepare to read |input_files| */ { int i; @<Get the master file started@>; actual_input=0; no_ch=0; while (++no_ch<next_available_file) { if (!file_open(&(input_files[no_ch]),input_organization[no_ch].name_of_file)) { fatal_error("! Change file can\’t be opened"); @.Change file can’t be opened@> else { print("("); print_name_of_file(no_ch); print_ln(")"); init_change_file(no_ch,true);

if (–no_ch==0) fatal_error("! No change file found"); @.No change file found@> for (i=next_available_file; i<=max_file_index; i++) input_organization[i].mode=ignore;

@ The master file is actually started just like the change files. @<Get the master file started@>= if (!file_open(&(input_files[0]),input_organization[0].name_of_file)) { fatal_error("! Master file can\’t be opened"); @.Master file can’t be opened@> else { print("("); print_name_of_file(0); print_ln(")"); input_organization[0].type_of_file = master; get_line(0);

@ The main output goes to |out_file|. @d out_file = stdout

@ Procedure |init_change_file(i,b)| is used to ignore all lines of the input file with index |i| until the next change module is found. The boolean parameter |b| indicates whether we do not want to see "@@x" or "@@y" entries during our skip.

@<Procedures for |open_input|@>= init_change_file(i,b) int i; boolean b; { @<Skip over comment lines; |return| if end of file@>; @<Skip to the next nonblank line; |return| if end of file@>;

@ While looking for a line that begins with \.{@@x in the change file, we allow lines that begin with \.{@@, as long as they don"t begin with \.{@@y or \.{@@z (which would probably indicate that the change file is fouled up).

@<Skip over comment lines...@>= loop { get_line(i); if (input_organization[i].mode==ignore) return; if (input_organization[i].limit<2) continue; if (input_organization[i].buffer[0] != ’@@’) continue; if ((input_organization[i].buffer[1]>=’X’) && (input_organization[i].buffer[1]<=’Z’)) input_organization[i].buffer[1] += ’z’-’Z’; /* lowercasify */ if (input_organization[i].buffer[1]==’x’) break; if ((input_organization[i].buffer[1]==’y’) || (input_organization[i].buffer[1]==’z’)) if (b) /* waiting for start of change */ err_print("! Where is the matching @@x?",i); @.Where is the match...@>

@ Here we are looking at lines following the \.{@@x.

@<Skip to the next nonblank line...@>= do { get_line(i); if (input_organization[i].mode==ignore) { err_print("! Change file ended after @@x",i); @.Change file ended...@> return; while (input_organization[i].limit<=0);

@ The |put_line| procedure is used to write a line from input buffer |j| to the output file.

@c put_line(j) int j; { int i; /* index into the buffer */ for (i=0; i<input_organization[j].limit; i++) fputc(xchr[input_organization[j].buffer[i]],out_file); fputc(’\n’,out_file);

@ Writing strings to the output @c write_string(fp, s) text_file fp; char *s; { while (*s != ’\0’) { putc(*s,fp);s++;

@ To start with our inputs we set the indication that all change files are in state |search|. That means that we must skip to the next (in this case the first) change entry.

@<Initialize the input system@>= common_init(); for (i=0; i<next_available_file; i++) { input_organization[i].mode=search; input_organization[i].lineno=0; input_organization[i].type_of_file=chf; input_organization[i].limit=0; for (j=0 ; j<= buf_size; j++) input_organization[i].buffer[j]=0; actual_input=0; out_mode=normal; open_input();

@*The major line-eating routines. @ To process the input file the procedure |process_line| reads a line of the actual input file and updates the |input_organization| for all files with index |i| greater |actual_input|.

@c process_line() { int i; @<Count lines processed, and shout at each hundred@>; @<Set |actual_input| and |test_input|, and set their modes@>; @<Write one (decorated) line of output, and set output mode@>; @<Read lines from |actual_input| (and |test_input| if necessary, checking for preamble end)@>;

@ If |test_input| is |none|, no change file is tested.

@d none = (next_available_file)

@<Set |actual_input| and |test_input|, and set their modes@>= while (e_of_ch_module(actual_input)) { if (input_organization[actual_input].type_of_file==master) /* emergency exit, everything mixed up! */ fatal_error("! This can\’t happen"); input_organization[actual_input].mode=search; init_change_file(actual_input,true); while ((input_organization[actual_input].mode!=reading) && (actual_input>0)) actual_input–; if (input_has_ended && (actual_input==0)) return; test_input=none; i=actual_input; while ((test_input==none) && (i<no_ch)) { i++; switch (input_organization[i].mode) { case search: if (!lines_dont_match(actual_input,i)) { input_organization[i].mode=test; test_input=i; break; case test: if (lines_dont_match(actual_input, i)) { /* error, modules do not match */ input_organization[i].mode=search; err_print("! Sections do not match",actual_input); @.Sections do not match@> err_loc(i); init_change_file(i,false); else test_input=i; break; case reading:do_nothing; /* this can’t happen */ break; case ignore: do_nothing; /* nothing to do */ break;

@ @<Write one (decorated) line of output, and set output mode@>= if (prod_chf) { loop { @<Test for normal@>; @<Test for pre@>; @<Test for post@>; else if (test_input==none) put_line(actual_input);

@ Check whether we have to start a change file entry.

@<Test for normal@>= { if (out_mode==normal) if (test_input!=none) { write_string(out_file,"@@x\n"); out_mode=pre; else break;

@ Check whether we have to start the replacement text.

@<Test for pre@>= { if (out_mode==pre) { if (test_input==none) { write_string(out_file,"@@y\n"); out_mode=post; else { if (input_organization[actual_input].type_of_file==master) put_line(actual_input); break;

@ Check whether a change file entry is complete.

@<Test for post@>= { if (out_mode==post) { if (input_organization[actual_input].type_of_file==chf) { if (test_input==none) put_line(actual_input); break; else { write_string(out_file,"@@z\n"); out_mode=normal;

@ @<Read lines from |actual_input| (and |test_input| if necessary, checking for preamble end)@>= get_line(actual_input); if (test_input!=none) { get_line(test_input); if (e_of_ch_preamble(test_input)) { get_line(test_input); /* update input file */ input_organization[test_input].mode=reading; actual_input=test_input; test_input=none;

@ We shout at the user about lines processed... @<Count lines proc...@>= if (++lines_processed % 100 == 0) { if (lines_processed % 500 == 0) fprintf(stderr,"%d",lines_processed); else fprintf(stderr,".");

@ @<Globals in...@>=long int lines_processed=0; @ @<Write carriage return...@>=fprintf(stderr,"\n");

@i io.web @i history.web @i error.web @i character.web

@* System-dependent changes.

\noindent This section should be replaced, if necessary, by changes to the program that are necessary to make \.{TIE work at a particular installation. It is usually best to design your change file so that all changes to previous modules preserve the module numbering; then everybody’s version will be consistent with the printed program. More extensive changes, which introduce new modules, can be inserted here; then only the index itself will get a new module number. @^system dependencies@>

@<System-dependent parts of char...@>= xchr[’\t’]=’\t’;

@* Index.

\noindent Here is the cross-reference table for the \.{TIE processor.  


This document was generated on January 31, 2023 using texi2html 5.0.